﻿using System.ComponentModel;
using System;
using System.Collections.Generic;

namespace SchoolDomain
{
    /// <summary>
    /// Вспомогательная фабрика для вычислений по оценкам в разрезе учеников.
    /// </summary>
    internal class MarkCalculatorFactory
    {
        /// <summary>
        /// Вспомогательный класс для расчета среднего балла по ученикам.
        /// </summary>
        class CountSum
        {
            /// <summary>
            /// Количество оценок.
            /// </summary>
            private int count;
            /// <summary>
            /// Количество оценок.
            /// </summary>
            public int Count
            {
                get { return count; }
                set { count = value; }
            }

            /// <summary>
            /// Сумма оценок.
            /// </summary>
            private int sum;
            /// <summary>
            /// Сумма оценок.
            /// </summary>
            public int Sum
            {
                get { return sum; }
                set { sum = value; }
            }

            /// <summary>
            /// Рейтинг учебной успешности.
            /// </summary>
            private int rating;
            /// <summary>
            /// Рейтинг учебной успешности.
            /// </summary>
            public int Rating
            {
                get { return rating; }
                set { rating = value; }
            }

            /// <summary>
            /// Создать вспомогательный класс.
            /// </summary>
            /// <param name="count">число оценок</param>
            /// <param name="sum">сумма оценок</param>
            /// <param name="rating">рейтинг учебной успешности</param>
            public CountSum(int count, int sum, int rating)
            {
                this.count = count;
                this.sum = sum;
                this.rating = Mark.START_RATING + rating;
            }
        };

        /// <summary>
        /// Экземпляр объекта.
        /// </summary>
        private static MarkCalculatorFactory instance;
        /// <summary>
        /// Экземпляр объекта.
        /// </summary>
        public static MarkCalculatorFactory Instance
        {
            get
            {
                if (instance == null)
                    instance = new MarkCalculatorFactory();
                return instance;
            }
        }

        /// <summary>
        /// Словарь суммарных баллов по ученикам.
        /// </summary>
        private Dictionary<Student, CountSum> sumMarkDictionary;

        /// <summary>
        /// Получить средний балл по ученику.
        /// </summary>
        /// <param name="student">ученик</param>
        /// <returns>средний балл по ученику</returns>
        public double GetStudentAverageMark(Student student)
        {
            if ((sumMarkDictionary.ContainsKey(student)) && (sumMarkDictionary[student].Count > 0))
                return (double)sumMarkDictionary[student].Sum / (double)sumMarkDictionary[student].Count;
            else
                return 0.0;
        }

        /// <summary>
        /// Получить рейтинг учебной успешности по ученику.
        /// </summary>
        /// <param name="student">ученик</param>
        /// <returns>рейтинг учебной успешности по ученику</returns>
        public int? GetStudentRating(Student student)
        {
            if (sumMarkDictionary.ContainsKey(student))
                return sumMarkDictionary[student].Rating;
            else
                return Mark.START_RATING;
        }

        /// <summary>
        /// Создать фабрику калькуляторов среднего балла по ученику.
        /// </summary>
        internal MarkCalculatorFactory()
        {
            sumMarkDictionary = new Dictionary<Student, CountSum>();
            MarkFactory.Instance.Marks.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Marks_CollectionChanged);
        }

        /// <summary>
        /// Обработать изменение коллекции оценок.
        /// </summary>
        /// <param name="sender">источник события</param>
        /// <param name="e">аргументы события</param>
        private void Marks_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if ((e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add) ||
                (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Replace))
            {
                foreach (Mark mark in e.NewItems)
                    mark.PropertyChanged += new PropertyChangedEventHandler(Mark_PropertyChanged);
            }
            calculateAverageMark();
        }

        /// <summary>
        /// Обработать изменение значения оценки.
        /// </summary>
        /// <param name="sender">источник события</param>
        /// <param name="e">аргументы события</param>
        void Mark_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            calculateAverageMark();
        }

        /// <summary>
        /// Пересчитать значение среднего балла.
        /// </summary>
        private void calculateAverageMark()
        {
            sumMarkDictionary.Clear();
            for (int itemIndex = 0; itemIndex < MarkFactory.Instance.Marks.Count; itemIndex++)
            {
                Mark mark = MarkFactory.Instance.Marks[itemIndex];
                int markValue = 0;
                int markCount = 0;
                if (mark.StandardMark != null)
                {
                    markValue = (int)mark.StandardMark;
                    markCount = 1;
                }
                int rating = mark.GetRating();
                if (sumMarkDictionary.ContainsKey(mark.Student))
                {
                    sumMarkDictionary[mark.Student].Sum = sumMarkDictionary[mark.Student].Sum + markValue;
                    sumMarkDictionary[mark.Student].Rating = sumMarkDictionary[mark.Student].Rating + rating;
                    sumMarkDictionary[mark.Student].Count = sumMarkDictionary[mark.Student].Count + markCount;
                }
                else
                    sumMarkDictionary.Add(mark.Student, new CountSum(markCount, markValue, rating));
            }
            OnMarkChanged();
        }

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Событие на изменение значения среднего балла.
        /// </summary>
        public event EventHandler MarkChanged;

        /// <summary>
        /// Генерация события на изменение значения свойства.
        /// </summary>
        private void OnMarkChanged()
        {
            if (MarkChanged != null)
            {
                MarkChanged(this, new EventArgs());
            }
        }

        #endregion
    }

    /// <summary>
    /// Класс-вычислитель среднего балла по ученикам.
    /// </summary>
    public class AverageMarkCalculator : INotifyPropertyChanged
    {
        /// <summary>
        /// Ученик, по которому вычисляется средний балл.
        /// </summary>
        private Student student;
        /// <summary>
        /// Значение среднего балла.
        /// </summary>
        private double averageMark;
        /// <summary>
        /// Значение среднего балла.
        /// </summary>
        public double AverageMark
        {
            get { return averageMark; }
            set
            {
                averageMark = value;
                OnPropertyChanged("AverageMark");
            }
        }

        /// <summary>
        /// Создать калькулятор среднего балла по ученику.
        /// </summary>
        /// <param name="student">ученик</param>
        public AverageMarkCalculator(Student student)
        {
            this.student = student;
            this.averageMark = 0.0;
            MarkCalculatorFactory.Instance.MarkChanged += new EventHandler(Instance_MarkChanged);
        }

        /// <summary>
        /// Обработать событие на изменение среднего балла.
        /// </summary>
        /// <param name="sender">источник события</param>
        /// <param name="e">аргменты события</param>
        void Instance_MarkChanged(object sender, EventArgs e)
        {
            AverageMark = MarkCalculatorFactory.Instance.GetStudentAverageMark(student);
        }

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Событие на изменение значения свойства.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Генерация события на изменение значения свойства.
        /// </summary>
        /// <param name="propertyName">имя свойства</param>
        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion
    }

    /// <summary>
    /// Класс-вычислитель рейтинга успешности ученика.
    /// </summary>
    public class RatingCalculator : INotifyPropertyChanged
    {
        /// <summary>
        /// Ученик, по которому вычисляется средний балл.
        /// </summary>
        private Student student;
        /// <summary>
        /// Значение рейтинга учебной успешности.
        /// </summary>
        private int? rating;
        /// <summary>
        /// Значение рейтинга учебной успешности.
        /// </summary>
        public int? Rating
        {
            get
            {
                if (student.Name == "")
                    return null;
                return rating;
            }
            set
            {
                rating = value;
                OnPropertyChanged("Rating");
            }
        }

        /// <summary>
        /// Создать калькулятор среднего балла по ученику.
        /// </summary>
        /// <param name="student">ученик</param>
        public RatingCalculator(Student student)
        {
            this.student = student;
            this.rating = Mark.START_RATING;
            student.PropertyChanged += new PropertyChangedEventHandler(student_PropertyChanged);
            MarkCalculatorFactory.Instance.MarkChanged += new EventHandler(Instance_MarkChanged);
        }

        /// <summary>
        /// Обработать событие на изменение имени ученика.
        /// </summary>
        /// <param name="sender">источник события</param>
        /// <param name="e">аргументы события</param>
        private void student_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            calculateRating();
        }

        /// <summary>
        /// Обработать событие на изменение среднего балла.
        /// </summary>
        /// <param name="sender">источник события</param>
        /// <param name="e">аргменты события</param>
        private void Instance_MarkChanged(object sender, EventArgs e)
        {
            calculateRating();
        }

        /// <summary>
        /// Пересчитать рейтинг.
        /// </summary>
        private void calculateRating()
        {
            Rating = MarkCalculatorFactory.Instance.GetStudentRating(student);
        }

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Событие на изменение значения свойства.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Генерация события на изменение значения свойства.
        /// </summary>
        /// <param name="propertyName">имя свойства</param>
        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion
    }
}